using Fantasy.SourceGenerator.Common;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Collections.Generic;
using System.Linq;
using System.Text;
#pragma warning disable CS8602 // Dereference of a possibly null reference.

namespace Fantasy.SourceGenerator.Generators
{
    /// <summary>
    /// Entity System 注册代码生成器
    /// 自动生成 EntityComponent 所需的 System 注册代码，替代运行时反射
    /// </summary>
    [Generator]
    public partial class EntitySystemGenerator : IIncrementalGenerator
    {
        public void Initialize(IncrementalGeneratorInitializationContext context)
        {
            // 查找所有实现了 IEntitySystem 相关接口的类
            var systemTypes = context.SyntaxProvider
                .CreateSyntaxProvider(
                    predicate: static (node, _) => IsSystemClass(node),
                    transform: static (ctx, _) => GetSystemTypeInfo(ctx))
                .Where(static info => info != null)
                .Collect();
            // 组合编译信息和找到的类型
            var compilationAndTypes = context.CompilationProvider.Combine(systemTypes);
            // 注册源代码输出
            context.RegisterSourceOutput(compilationAndTypes, static (spc, source) =>
            {
                // 检查1: 是否定义了 FANTASY_NET 或 FANTASY_UNITY 预编译符号
                if (!CompilationHelper.HasFantasyDefine(source.Left))
                {
                    return;
                }

                // 检查2: 是否引用了 Fantasy 框架的核心类型
                if (source.Left.GetTypeByMetadataName("Fantasy.Assembly.IEntitySystemRegistrar") == null)
                {
                    return;
                }

                GenerateRegistrationCode(spc, source.Left, source.Right!);
            });
        }
        
        private static EntitySystemTypeInfo? GetSystemTypeInfo(GeneratorSyntaxContext context)
        {
            var classDecl = (ClassDeclarationSyntax)context.Node;

            if (context.SemanticModel.GetDeclaredSymbol(classDecl) is not INamedTypeSymbol symbol || !symbol.IsInstantiable())
            {
                return null;
            }
            
            var baseType = symbol.BaseType;

            if (!baseType.IsGenericType)
            {
                return null;
            }
       
            return baseType.Name switch
            {
                "AwakeSystem" => EntitySystemTypeInfo.Create(EntitySystemType.AwakeSystem, symbol),
                "UpdateSystem" => EntitySystemTypeInfo.Create(EntitySystemType.UpdateSystem, symbol),
                "DestroySystem" => EntitySystemTypeInfo.Create(EntitySystemType.DestroySystem, symbol),
                "DeserializeSystem" => EntitySystemTypeInfo.Create(EntitySystemType.DeserializeSystem, symbol),
                "LateUpdateSystem" => EntitySystemTypeInfo.Create(EntitySystemType.LateUpdateSystem, symbol),
                _ => null
            };
        }
        
        private static void GenerateRegistrationCode(
            SourceProductionContext context,
            Compilation compilation,
            IEnumerable<EntitySystemTypeInfo> systemTypes)
        {
            var entitySystemTypeInfos = systemTypes.ToList();
            // 获取当前程序集名称（仅用于注释）
            var assemblyName = compilation.AssemblyName ?? "Unknown";
            // 生成代码文件
            var builder = new SourceCodeBuilder();
            // 添加文件头
            builder.AppendLine(GeneratorConstants.AutoGeneratedHeader);
            // 添加 using
            builder.AddUsings(
                "System",
                "System.Collections.Generic",
                "Fantasy.Assembly",
                "Fantasy.Entitas",
                "Fantasy.Entitas.Interface"
            );
            builder.AppendLine();
            // 开始命名空间（固定使用 Fantasy.Generated）
            builder.BeginNamespace("Fantasy.Generated");
            // 开始类定义（实现 IEntitySystemRegistrar 接口）
            builder.AddXmlComment($"Auto-generated Entity System registration class for {assemblyName}");
            builder.BeginClass("EntitySystemRegistrar", "internal sealed", "IEntitySystemRegistrar");
            // 生成字段用于存储已注册（用于 UnRegister）
            GenerateFields(builder, entitySystemTypeInfos);
            // 生成注册方法（标准版本）
            GenerateRegisterMethod(builder, entitySystemTypeInfos);
            // 生成反注册方法（标准版本）
            GenerateUnRegisterMethod(builder, entitySystemTypeInfos);
            // 生成 Dispose 方法
            GenerateDisposeMethod(builder, entitySystemTypeInfos);
            // 结束类和命名空间
            builder.EndClass();
            builder.EndNamespace();
            // 输出源代码
            context.AddSource("EntitySystemRegistrar.g.cs", builder.ToString());
        }
        
        private static void GenerateFields(SourceCodeBuilder builder, List<EntitySystemTypeInfo> entitySystemTypeInfos)
        {
            builder.AddComment("Store registered entity system for UnRegister");

            if (!entitySystemTypeInfos.Any())
            {
                return;
            }
            
            foreach (var eventSystemTypeInfo in entitySystemTypeInfos)
            {
                var fieldName = $"_{eventSystemTypeInfo.TypeName.ToCamelCase()}";
                builder.AppendLine($"private {eventSystemTypeInfo.TypeFullName} {fieldName} = new {eventSystemTypeInfo.TypeFullName}();");
            }
            
            builder.AppendLine();
        }
        
        private static void GenerateRegisterMethod(SourceCodeBuilder builder, List<EntitySystemTypeInfo> entitySystemTypeInfos)
        {
            builder.AddXmlComment("Register all Entity Systems to the dictionaries");
            builder.AppendLine("#if FANTASY_NET", false);
            builder.BeginMethod(
                "public void RegisterSystems(" +
                "Dictionary<Type, IAwakeSystem> awakeSystems, " +
                "Dictionary<Type, IUpdateSystem> updateSystems, " +
                "Dictionary<Type, IDestroySystem> destroySystems, " +
                "Dictionary<Type, IDeserializeSystem> deserializeSystems)");
            builder.AppendLine("#endif", false);
            builder.Unindent();
            builder.AppendLine("#if FANTASY_UNITY", false);
            builder.BeginMethod(
                "public void RegisterSystems(" +
                "Dictionary<Type, IAwakeSystem> awakeSystems, " +
                "Dictionary<Type, IUpdateSystem> updateSystems, " +
                "Dictionary<Type, IDestroySystem> destroySystems, " +
                "Dictionary<Type, IDeserializeSystem> deserializeSystems, " +
                "Dictionary<Type, ILateUpdateSystem> lateUpdateSystems)");
            builder.AppendLine("#endif", false);
            
            if (entitySystemTypeInfos.Any())
            {
                foreach (var systemTypeInfo in entitySystemTypeInfos)
                {
                    var fieldName = $"_{systemTypeInfo.TypeName.ToCamelCase()}";

                    switch (systemTypeInfo.EntitySystemType)
                    {
                        case EntitySystemType.AwakeSystem:
                        {
                            builder.AppendLine($"awakeSystems.Add({fieldName}.EntityType(), {fieldName});");
                            continue;
                        }
                        case EntitySystemType.UpdateSystem:
                        {
                            builder.AppendLine($"updateSystems.Add({fieldName}.EntityType(), {fieldName});");
                            continue;
                        }
                        case EntitySystemType.DestroySystem:
                        {
                            builder.AppendLine($"destroySystems.Add({fieldName}.EntityType(), {fieldName});");
                            continue;
                        }
                        case EntitySystemType.DeserializeSystem:
                        {
                            builder.AppendLine($"deserializeSystems.Add({fieldName}.EntityType(), {fieldName});");
                            continue;
                        }
                        // case EntitySystemType.AwakeSystem:
                        // {
                        //     builder.AppendLine($"awakeSystems.Add({fieldName}.EntityType(), {fieldName});");
                        //     continue;
                        // }
                    }
                }
            }
            
            builder.EndMethod();
            builder.AppendLine();
        }
        
        private static void GenerateUnRegisterMethod(SourceCodeBuilder builder, List<EntitySystemTypeInfo> entitySystemTypeInfos)
        {
            builder.AddXmlComment("Unregister all Entity Systems from the dictionaries");
            builder.AppendLine("#if FANTASY_NET", false);
            builder.BeginMethod(
                "public void UnRegisterSystems(" +
                "Dictionary<Type, IAwakeSystem> awakeSystems, " +
                "Dictionary<Type, IUpdateSystem> updateSystems, " +
                "Dictionary<Type, IDestroySystem> destroySystems, " +
                "Dictionary<Type, IDeserializeSystem> deserializeSystems)");
            builder.AppendLine("#endif", false);
            builder.Unindent();
            builder.AppendLine("#if FANTASY_UNITY", false);
            builder.BeginMethod(
                "public void UnRegisterSystems(" +
                "Dictionary<Type, IAwakeSystem> awakeSystems, " +
                "Dictionary<Type, IUpdateSystem> updateSystems, " +
                "Dictionary<Type, IDestroySystem> destroySystems, " +
                "Dictionary<Type, IDeserializeSystem> deserializeSystems, " +
                "Dictionary<Type, ILateUpdateSystem> lateUpdateSystems)");
            builder.AppendLine("#endif", false);

            if (entitySystemTypeInfos.Any())
            {
                foreach (var systemTypeInfo in entitySystemTypeInfos)
                {
                    var fieldName = $"_{systemTypeInfo.TypeName.ToCamelCase()}";

                    switch (systemTypeInfo.EntitySystemType)
                    {
                        case EntitySystemType.AwakeSystem:
                        {
                            builder.AppendLine($"awakeSystems.Remove({fieldName}.EntityType());");
                            continue;
                        }
                        case EntitySystemType.UpdateSystem:
                        {
                            builder.AppendLine($"updateSystems.Remove({fieldName}.EntityType());");
                            continue;
                        }
                        case EntitySystemType.DestroySystem:
                        {
                            builder.AppendLine($"destroySystems.Remove({fieldName}.EntityType());");
                            continue;
                        }
                        case EntitySystemType.DeserializeSystem:
                        {
                            builder.AppendLine($"deserializeSystems.Remove({fieldName}.EntityType());");
                            continue;
                        }
                        // case EntitySystemType.LateUpdateSystem:
                        // {
                        //     builder.AppendLine($"lateUpdateSystem.Remove({fieldName}.EntityType());");
                        //     continue;
                        // }
                    }
                }
            }
            
            builder.EndMethod();
            builder.AppendLine();
        }
        
        /// <summary>
        /// 生成 Dispose 方法
        /// </summary>
        private static void GenerateDisposeMethod(SourceCodeBuilder builder, List<EntitySystemTypeInfo> entitySystemTypeInfos)
        {
            builder.AddXmlComment("Dispose the registrar (required by IDisposable)");
            builder.BeginMethod("public void Dispose()");
            
            if (entitySystemTypeInfos.Any())
            {
                foreach (var systemTypeInfo in entitySystemTypeInfos)
                {
                    var fieldName = $"_{systemTypeInfo.TypeName.ToCamelCase()}";
                    builder.AppendLine($"{fieldName} = null;");
                }
            }
            
            builder.EndMethod();
        }
        
        private static bool IsSystemClass(SyntaxNode node)
        {
            if (node is not ClassDeclarationSyntax classDecl)
            {
                return false;
            }

            // 必须有基类型列表（继承抽象类）
            if (classDecl.BaseList == null || classDecl.BaseList.Types.Count == 0)
            {
                return false;
            }
            
            // 快速检查是否包含可能的 EntitySystem 基类名称
            var baseListText = classDecl.BaseList.ToString();
            return baseListText.Contains("AwakeSystem") ||
                   baseListText.Contains("UpdateSystem") ||
                   baseListText.Contains("DestroySystem") ||
                   baseListText.Contains("DeserializeSystem") ||
                   baseListText.Contains("LateUpdateSystem");
        }
        
        private enum EntitySystemType
        {
            None,
            AwakeSystem,
            UpdateSystem,
            DestroySystem,
            DeserializeSystem,
            LateUpdateSystem
        }
        
        private sealed class EntitySystemTypeInfo
        {
            public readonly EntitySystemType EntitySystemType;
            public readonly string TypeFullName;
            public readonly string TypeName;
            private EntitySystemTypeInfo(EntitySystemType entitySystemType, string typeFullName, string typeName)
            {
                EntitySystemType = entitySystemType;
                TypeFullName = typeFullName;
                TypeName = typeName;
            }

            public static EntitySystemTypeInfo Create(EntitySystemType entitySystemType, INamedTypeSymbol symbol)
            {
                return new EntitySystemTypeInfo(
                    entitySystemType,
                    symbol.GetFullName(),
                    symbol.Name);
            }
        }
    }
}
